﻿using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Text;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    interface IXLString
    {}
    internal struct XLUnicodeString : IXLString
    {
        public static explicit operator XLUnicodeString(string s)
        {
            return new XLUnicodeString(){_value = s};
        }

        public ushort Cch
        {
            get { return _value == null ? (ushort) 0 : (ushort) _value.Length; }
        }

        public override string ToString()
        {
            return string.Format("{0}({1})", this._value, this.fHighByte);
        }
        public static explicit operator string (XLUnicodeString s)
        {
            return s._value;
        } 
        private byte fHighByte;
        private string _value;


        public void Read(BinaryReader reader)
        {
#if DEBUG
            var position = reader.BaseStream.Position;
       //  removed, XL uses invalid characters for '?' 
            // ReadWriteChecker.CheckStruct((ref XLUnicodeString t, BinaryReader r) => t.Read(r),
         //                                                 (ref XLUnicodeString t, BinaryWriter w) => t.Write(w), reader, ref this);
#endif
            ushort cch = reader.ReadUInt16();
            fHighByte = reader.ReadByte();
            switch (fHighByte)
            {
                case 0:
                    {
                        byte[] bytes = reader.ReadBytes(cch);
                        _value = Encoding.ASCII.GetString(bytes);
                    }
                    break;
                default:
                    {
                        byte[] bytes = reader.ReadBytes(cch*2);
                        _value = XlEncodings.TwoByteEncoding.GetString(bytes);
                    }
                    break;
            }
        }

        public void Read(BinaryReader reader, ushort cch)
        {
            if (cch == 0)
                return;
            fHighByte = reader.ReadByte();
            switch (fHighByte)
            {
                case 0:
                    {
                        byte[] bytes = reader.ReadBytes(cch);
                        _value = Encoding.ASCII.GetString(bytes);
                    }
                    break;
                default:
                    {
                        byte[] bytes = reader.ReadBytes(cch * 2);
                        _value = XlEncodings.TwoByteEncoding.GetString(bytes);
                    }
                    break;
            }
        }
        public void Write(BiffWriterWorkbookContext writer)
        {
            writer.Write((ushort)_value.Length);
            writer.Write(fHighByte);
            if (this.fHighByte == 0)
            {
                writer.Write(Encoding.ASCII.GetBytes(_value));
            }
            else
            {
                writer.Write(XlEncodings.TwoByteEncoding.GetBytes(_value));
                
            }
        }
        public void Write(BinaryWriter writer)
        {
#if DEBUG
            var position = writer.BaseStream.Position;
#endif
            ushort len = Cch;
            Contract.Assert( (len == ( _value == null ? (ushort)0 : (ushort) _value.Length)));
            writer.Write(len);
            writer.Write(fHighByte);
            if (_value == null)
                return;
            if (this.fHighByte == 0)
            {
                writer.Write(Encoding.ASCII.GetBytes(_value));
            }
            else
            {
                writer.Write(XlEncodings.TwoByteEncoding.GetBytes(_value));

            }

        }

        public void WriteCharCount(BinaryWriter writer)
        {
            ushort u = (ushort) (_value == null ? 0 : _value.Length);
            writer.Write(u);
        }

        public void WriteNoLength(BinaryWriter writer)
        {
            if (_value == null)
                return;
            writer.Write(fHighByte);
            if (this.fHighByte == 0)
            {
                writer.Write(Encoding.ASCII.GetBytes(_value));
            }
            else
            {
                writer.Write(XlEncodings.TwoByteEncoding.GetBytes(_value));

            }
        }

        public string CleanString()
        {
            var result = (string) this;
            if (result == null)
                return result;
            result = result.Replace("\0", "");
            if (string.IsNullOrEmpty(result))
                return null;
            return result;
            
        }
    }
}